/*
 * @file_name: http_session.cpp
 * @date: 2021/12/06
 * @author: oaho
 * Copyright @ hz oaho, All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.x
 */
#include <Util/logger.h>
#include <http/http_response.hpp>
#include <http/http_session.hpp>
#include <map>
#include <string.h>
#include <utility>
#include <vector>
namespace http {
using namespace toolkit;
http_session::http_session(const Socket::Ptr &sock)
    : toolkit::TcpSession(sock) {}

void http_session::onRecv(const Buffer::Ptr &buf) {
  std::shared_ptr<string_request_type> request =
      std::make_shared<string_request_type>();
  buffer.append(buf->data(), buf->size());
  // drain 找到了\r\n\r\n
  size_t drain = input(buffer.data(), buffer.size());
  if (!drain || drain == std::string::npos)
    return;
  //解析http头
  try {
    parse_header_internal(request, buffer.data(), drain + 2);
    // http body
    drain += 4;
    if (request->has_header(http::field::content_length)) {
      size_t body_length =
          std::stoull(request->get_header(http::field::content_length));
      auto http_body_size = buffer.size() - drain;
      //如果http消息体数据还没有读取完毕
      if (http_body_size < body_length)
        return;
      request->body().append(buf->data() + drain, body_length);
      //移除body消息
      buffer.erase(0, body_length);
    }
    //移除头部消息
    buffer.erase(0, drain);
    on_recv_http(request);
  } catch (const std::exception &e) {
    ErrorL << e.what();
    //清理缓冲
    buffer.clear();
  }
}
void http_session::onError(const SockException &err) { ErrorL << err.what(); }

size_t http_session::input(const void *data, size_t len) {
  const char *begin = (const char *)data;
  const char *end = strnstr(begin, "\r\n\r\n", len);
  if (end == nullptr)
    return 0;
  return end - begin;
}

void http_session::send_response(
    std::shared_ptr<http::http_response<http::string_body>> resp) {
  auto buf = resp->to_string();
  SockSender::send(buf->data(), buf->size());
}
/*
 * GET /path
 *  \r \n \r \n
 *  0  1  2  3
 * */
size_t http_session::parse_header_internal(
    std::shared_ptr<string_request_type> &request, const void *data,
    size_t len) {
  if (len == 0)
    return 0;
  //解析请求方法
  std::string str((const char *)data, len);
  std::vector<std::string> content = std::move(toolkit::split(str, "\r\n"));
  if (!content.size())
    throw std::logic_error("错误的http请求: 解析头部字段失败");
  auto &request_line = content[0];

  size_t current_pos = 0;
  size_t last_pos = 0;
  current_pos = request_line.find(" ");
  if (current_pos == std::string::npos)
    throw std::logic_error("错误的http请求: 未找到请求方法");
  std::string method = request_line.substr(0, current_pos);
  if (method == http::_method::get)
    request->method(http::_method::get);
  else if (method == http::_method::post)
    request->method(http::_method::post);
  else if (method == http::_method::put)
    request->method(http::_method::put);
  else if (method == http::_method::del)
    request->method(http::_method::del);
  else
    throw std::logic_error("错误的http请求的方法: 请求方法不支持");

  ++current_pos;
  last_pos = current_pos;
  current_pos = request_line.find(" ", current_pos);
  if (current_pos == std::string::npos)
    throw std::logic_error("错误的http请求方法: 未找到url");
  request->set_path(request_line.substr(last_pos, current_pos - last_pos));

  ++current_pos;
  int ret = 0;
  if (strncmp(request_line.data() + current_pos, "HTTP/1.1", 8) != 0 &&
      request_line.size() - current_pos != 8)
    throw std::logic_error("错误的http请求: 不支持的协议版本");

  for (size_t i = 1; i < content.size(); i++) {
    auto &header_line = content[i];
    auto pos = header_line.find(":");
    if (pos == std::string::npos)
      throw std::logic_error("错误的http请求头");
    request->set_header(
        header_line.substr(0, pos),
        header_line.substr(pos + 2, header_line.size() - pos - 2));
  }
  return 0;
}
void http_session::on_recv_http(
    std::shared_ptr<http::http_request<http::string_body>> request) {
  std::shared_ptr<http::http_response<http::string_body>> response(
      new http::http_response<http::string_body>);

  const auto &path = request->get_path();
  auto pos = path.find("?");
  std::string real_path;
  std::map<std::string, std::string> param;
  if (pos != std::string::npos) {
    real_path = path.substr(0, pos);
  }
  ++pos;

  auto func = [&](const std::string &target, size_t &_begin, size_t &_last) {
    std::string line = target.substr(_last, _begin - _last);
    auto equal_symbol = line.find("=");
    if (equal_symbol == std::string::npos)
      return;
    param[line.substr(0, equal_symbol)] = line.substr(equal_symbol + 1);
    _last = _begin + 1;
  };

  if (path.size() > pos) {
    std::string parameters = path.substr(pos);
    size_t last_ = 0;
    size_t begin = 0;
    // name=shenhao&val=1
    while (begin < parameters.size()) {
      begin = parameters.find("&", last_);
      if (begin == std::string::npos) {
        if (last_ < parameters.size()) {
          func(parameters, begin, last_);
        }
        break;
      }
      func(parameters, begin, last_);
    }
  }
  if (end_with(real_path, ".m3u8"))
    return serve_m3u8(param, request);
  else if (end_with(real_path, ".flv"))
    return serve_flv(param, request);
} // namespace

void http_session::serve_m3u8(
    const std::map<std::string, std::string> &param,
    std::shared_ptr<http::http_request<http::string_body>> request) {}
void http_session::serve_flv(
    const std::map<std::string, std::string> &param,
    std::shared_ptr<http::http_request<http::string_body>> request) {}
} // namespace http